package org.michaelrenaud.pathfinder.client.core.app;

import java.util.ArrayList;
import java.util.List;

import org.michaelrenaud.pathfinder.client.event.CharacterChangeEvent;
import org.michaelrenaud.pathfinder.shared.action.GetCharacter;
import org.michaelrenaud.pathfinder.shared.action.GetCharacterResult;
import org.michaelrenaud.pathfinder.shared.model.Character;
import org.michaelrenaud.pathfinder.shared.model.Class;
import org.michaelrenaud.pathfinder.shared.model.HasEffects;
import org.michaelrenaud.pathfinder.shared.model.Skill;
import org.michaelrenaud.pathfinder.shared.model.Stat;
import org.michaelrenaud.pathfinder.shared.model.StatBlock;
import org.michaelrenaud.pathfinder.shared.model.StatBlockSkill;

import com.google.gwt.event.shared.EventBus;
import com.google.gwt.event.shared.GwtEvent.Type;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.inject.Inject;
import com.gwtplatform.dispatch.shared.DispatchAsync;
import com.gwtplatform.mvp.client.Presenter;
import com.gwtplatform.mvp.client.View;
import com.gwtplatform.mvp.client.annotations.ContentSlot;
import com.gwtplatform.mvp.client.annotations.ProxyCodeSplit;
import com.gwtplatform.mvp.client.proxy.Proxy;
import com.gwtplatform.mvp.client.proxy.RevealContentHandler;
import com.gwtplatform.mvp.client.proxy.RevealRootLayoutContentEvent;

public class AppPresenter extends Presenter<AppPresenter.MyView, AppPresenter.MyProxy> {

	@ContentSlot
	public static final Type<RevealContentHandler<?>> TYPE_RevealContent = new Type<RevealContentHandler<?>>();

	public interface MyView extends View {}

	@ProxyCodeSplit
	public interface MyProxy extends Proxy<AppPresenter> {}

	private final DispatchAsync dispatcher;
	protected Character character;
	protected StatBlock statBlock;

	@Inject
	public AppPresenter(
			final EventBus eventBus, 
			final MyView view, 
			final MyProxy proxy, 
			final DispatchAsync dispatcher) {
		super(eventBus, view, proxy);
		
		this.dispatcher = dispatcher;
	}

	@Override
	protected void revealInParent() {
		RevealRootLayoutContentEvent.fire(this, this);
	}

	@Override
	protected void onBind() {
		super.onBind();
		
		dispatcher.execute(new GetCharacter(), new AsyncCallback<GetCharacterResult>() {
			@Override
			public void onSuccess(GetCharacterResult result) {
				character = result.getCharacter();
				statBlock = new StatBlock();
				
				statBlock.setSizeModifier(getStat("Size Modifier", character.getRace().getSizeModifier(), true));
				
				// Ability Score
				statBlock.setStrength(getStat("Strength", character.getStrength(), false));
				statBlock.setDexterity(getStat("Dexterity", character.getDexterity(), false));
				statBlock.setConstitution(getStat("Constitution", character.getConstitution(), false));
				statBlock.setIntelligence(getStat("Intelligence", character.getIntelligence(), false));
				statBlock.setWisdom(getStat("Wisdom", character.getWisdom(), false));
				statBlock.setCharisma(getStat("Charisma", character.getCharisma(), false));
				
				// Item Bonus
				statBlock.setStrengthItemBonus(getEquipmentStat("Strength").getTotal());
				statBlock.setDexterityItemBonus(getEquipmentStat("Dexterity").getTotal());
				statBlock.setConstitutionItemBonus(getEquipmentStat("Constitution").getTotal());
				statBlock.setIntelligenceItemBonus(getEquipmentStat("Intelligence").getTotal());
				statBlock.setWisdomItemBonus(getEquipmentStat("Wisdom").getTotal());
				statBlock.setCharismaItemBonus(getEquipmentStat("Charisma").getTotal());

				// Skills
				for(Skill skill : character.getSkills()) {
					StatBlockSkill sbSkill = new StatBlockSkill(skill);
					sbSkill.setFeats(getStat(skill.getName(), 0, true));
					if(skill.getModifierName().equals("STR") || skill.getModifierName().equals("DEX"))
						sbSkill.setPenalty(getEquipmentStat("Armor Check Penalty"));

					for(Class c : character.getClasses()) {
						if((skill.getName().contains(":") && c.getClassSkills().contains(skill.getName().split(":")[0] + "*")) || 
								c.getClassSkills().contains(skill.getName())) {
							sbSkill.setClassSkill(true);
							sbSkill.getClasses().add(c.getName());
						}
					}
					int feats = sbSkill.getFeats().getTotal();
					int penalty = sbSkill.getPenalty().getTotal();
					
					sbSkill.setBonus(statBlock.getModifier(skill.getModifierName()) + (sbSkill.isClassSkill() && skill.getRanks() > 0 ? 3 : 0) + skill.getRanks() + feats - penalty);
					statBlock.getSkills().add(sbSkill);
				}
				
				// Init
				statBlock.setInitFeat(getStat("Initiative", 0, false));
				statBlock.setInitTraining(getStat("Initiative Training", 0, false));
				statBlock.setInitMisc(getEquipmentStat("Initiative"));
				statBlock.setInit(statBlock.getDEX() + statBlock.getInitFeat().getTotal() + statBlock.getInitTraining().getTotal() + statBlock.getInitMisc().getTotal());
				
				// Speeds
				statBlock.setBaseSpeed(getStat("Base Speed", character.getRace().getBaseSpeed(), true).getTotal());
				statBlock.setFlySpeed(getStat("Fly Speed", 0, true).getTotal());
				statBlock.setSwimSpeed(getStat("Swim Speed", 0, true).getTotal());
				statBlock.setClimbSpeed(getStat("Climb Speed", 0, true).getTotal());
				statBlock.setMountSpeed(getStat("Mount Speed", 0, true).getTotal());
				
				
				CharacterChangeEvent.fire(AppPresenter.this, character, statBlock);
			}

			@Override
			public void onFailure(Throwable caught) {
				// TODO Auto-generated method stub
				
			}
		});
	}

	private Stat getStat(String statName, int base, boolean includeEquipment) {
		Stat stat = new Stat();
		stat.setName(statName);
		stat.setBase(base);
		
		List<HasEffects> effectContainers = new ArrayList<HasEffects>();
		effectContainers.addAll(character.getRace().getTraits());
		effectContainers.addAll(character.getTraits());
		if(includeEquipment) {
			// TODO effectContainers.addAll(character.getEquipments());
		}
		
		for(HasEffects effectContainer : effectContainers) {
			if(effectContainer.getEffect(statName) != null) {
				String condition = effectContainer.getEffect(statName).getCondition();
				if(condition == null || condition.isEmpty())
					stat.getEffectContainers().add(effectContainer);
			}
		}
		
		return stat;
	}
	
	private Stat getEquipmentStat(String statName) {
		Stat stat = new Stat();
		stat.setName(statName);

		List<HasEffects> effectContainers = new ArrayList<HasEffects>();
		// TODO effectContainers.addAll(character.getEquipments());
		
		for(HasEffects effectContainer : effectContainers) {
			if(effectContainer.getEffect(statName) != null) {
				stat.getEffectContainers().add(effectContainer);
			}
		}
		
		return stat;
	}
}
